#pragma once

#include<iostream>
#include<string>
#include<unistd.h>
#include<sys/wait.h>
#include "../comm/log.hpp"
#include "../comm/util.hpp"
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/resource.h>
#include<sys/time.h>
namespace ns_runner
{
   using namespace ns_log;
   using namespace ns_util;

   class Runner
   {
   public:
      Runner(){}
      ~Runner(){}
   public:

   //提供设置进程占用资源大小的接口
   //参数int cup_limit 运行时间限制,运行的时候最大CPU资源上限
   //参数int men_limit 内存限制,可以使用最大内存的大小
   static void SetProcLimit(int _cpu_limit,int _mem_limit)
   {
      //设置CPU
      struct rlimit cpu_rlimit;
      cpu_rlimit.rlim_max=RLIM_INFINITY; //无穷
      cpu_rlimit.rlim_cur=_cpu_limit;
      setrlimit(RLIMIT_CPU,&cpu_rlimit);
      //设置内存大小
      struct rlimit mem_rlimit;
      mem_rlimit.rlim_max=RLIM_INFINITY;
      mem_rlimit.rlim_cur=_mem_limit*1024; //转化为kb
      setrlimit(RLIMIT_AS,&mem_rlimit);
   }
   
   /*
      指明文件名就行，不需要路径和后缀
      run跑完的结果对不对不关心，结果正确是由测试用例决定
      run只考虑是否正确运行   
      返回值 >0 :程序运行异常,退出的时候收到了信号,返回值就是信号编号
      返回值 ==0 :正常运行,结果保存在临时文件中
      返回值 <0  :内部错误 
   */
    static int Run(const std::string& file_name,int cpu_limit,int men_limit)
    {
      //运行的结果会写入到这些文件中
      std::string execute =PathUtil::Exe(file_name);
      std::string _stdin  =PathUtil::Stdin(file_name);
      std::string _stdout =PathUtil::Stdout(file_name);
      std::string _stderr =PathUtil::Stderr(file_name);

      umask(0);
      int _stdin_fd=open(_stdin.c_str(),O_CREAT|O_RDONLY,0644);
      int _stdout_fd=open(_stdout.c_str(),O_CREAT|O_WRONLY,0644);
      int _stderr_fd=open(_stderr.c_str(),O_CREAT|O_WRONLY,0644);
      
      if(_stdin_fd <0 ||_stdout_fd<0 || _stderr_fd<0)
      {
         LOG(ERROR) <<"运行时打开标准文件错误" <<"\n";
         return -1; //文件打开失败
      }

      pid_t pid=fork();
      if(pid<0)
      {
         LOG(ERROR) <<"运行时创建子进程失败" <<"\n";
         close(_stdin_fd);
         close(_stdout_fd);
         close(_stderr_fd);
         return -2; //创建子进程失败
      }
      else if(pid==0)
      {
         dup2(_stdin_fd,0);//标准输入重定向到_stdin_fd
         dup2(_stdout_fd,1);
         dup2(_stderr_fd,2);

         SetProcLimit(cpu_limit,men_limit);
         execl(execute.c_str()/*执行谁*/,execute.c_str()/*在命令行上如何执行*/);
         exit(1);
      }
      else
      {
         close(_stdin_fd);
         close(_stdout_fd);
         close(_stderr_fd);
         int status=0;
         waitpid(pid,&status,0);
         //程序运行异常一定是收到了信号
         LOG(INFO) <<"运行完毕,info:" << (status & 0x7F)<<"\n";
         return status & 0x7F;
      }
    }
   };
}